Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | 1x 1x 1x 1x 25x 3x 3x 3x 3x 3x 3x 3x 3x 3x 2x 2x 2x 2x 1x 1x 2x 1x 1x 5x 5x 5x 5x 5x 5x 5x 2x 1x 1x 1x 1x 1x 3x 3x 3x 1x 1x 1x 2x 2x 1x 1x 1x 1x 1x 1x 1x | import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { ROUTES, STORAGE_KEYS } from '@/constants';
// Define protected routes and their required roles
const PROTECTED_ROUTES = {
[ROUTES.ADMIN.BASE]: ['admin'],
[ROUTES.ADMIN.USERS]: ['admin'],
[ROUTES.ADMIN.CONTENT]: ['admin'],
[ROUTES.ADMIN.CATEGORIES]: ['admin'],
[ROUTES.ADMIN.SERIES]: ['admin'],
[ROUTES.ADMIN.ANALYTICS]: ['admin'],
[ROUTES.RESELLER.BASE]: ['admin', 'reseller'],
[ROUTES.RESELLER.CUSTOMERS]: ['admin', 'reseller'],
[ROUTES.RESELLER.CONTENT]: ['admin', 'reseller'],
[ROUTES.RESELLER.ANALYTICS]: ['admin', 'reseller'],
[ROUTES.USER.BASE]: ['admin', 'reseller', 'end_user'],
[ROUTES.USER.CONTENT]: ['admin', 'reseller', 'end_user'],
[ROUTES.USER.DEVICES]: ['admin', 'reseller', 'end_user'],
[ROUTES.USER.PROFILE]: ['admin', 'reseller', 'end_user']} as const;
// Public routes that don't require authentication
const PUBLIC_ROUTES = [
ROUTES.HOME,
ROUTES.LOGIN,
ROUTES.DEMO,
ROUTES.REDEEM,
'/banned', // Allow access to banned page for banned users
'/forgot-password',
'/reset-password',
];
function isPublicRoute(pathname: string): boolean {
return PUBLIC_ROUTES.some((route) => pathname === route || pathname.startsWith(`${route}/`));
}
function getRequiredRoles(pathname: string): string[] | null {
// Check exact match first
Eif (PROTECTED_ROUTES[pathname as keyof typeof PROTECTED_ROUTES]) {
return [...PROTECTED_ROUTES[pathname as keyof typeof PROTECTED_ROUTES]];
}
// Check if pathname starts with any protected route
for (const [route, roles] of Object.entries(PROTECTED_ROUTES)) {
if (pathname.startsWith(route)) {
return [...roles];
}
}
return null;
}
function getUserFromToken(token: string): { role: string } | null {
try {
// Decode JWT token payload (works both in edge and node runtimes)
const base = token.split('.')[1] || '';
const payloadJson = (() => {
try {
// prefer atob if available (edge/browser-like runtimes)
Eif (typeof atob === 'function') {
return atob(base);
}
} catch {
// fallthrough
}
// Node fallback
try {
return Buffer.from(base, 'base64').toString('utf-8');
} catch {
return '';
}
})();
if (!payloadJson) return null;
const payload = JSON.parse(payloadJson || '{}');
// Normalize role strings into canonical values used by the app
const rawRole = String(payload.role ?? '').trim();
const normalized = rawRole.toLowerCase().replace(/[-\s]/g, '_');
if (normalized === 'admin') return { role: 'admin' };
Iif (normalized === 'reseller') return { role: 'reseller' };
Eif (normalized === 'end_user' || normalized === 'enduser' || normalized.includes('user')) return { role: 'end_user' };
// Fallback: return normalized value
return { role: normalized };
} catch {
return null;
}
}
function getDefaultRouteForRole(role: string): string {
switch (role) {
case 'admin':
return ROUTES.ADMIN.BASE;
case 'reseller':
return ROUTES.RESELLER.BASE;
case 'end_user':
return ROUTES.USER.BASE;
default:
return ROUTES.HOME;
}
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
Iif (pathname === ROUTES.DEMO || pathname.startsWith(`${ROUTES.DEMO}/`) || pathname === ROUTES.REDEEM || pathname.startsWith(`${ROUTES.REDEEM}/`)) {
return NextResponse.next();
}
// Skip middleware entirely for static builds
Iif (process.env.NODE_ENV === 'production' && process.env.NEXT_STATIC_EXPORT === 'true') {
return NextResponse.next();
}
// Skip middleware for static files and API routes
Iif (
pathname.startsWith('/_next/') ||
pathname.startsWith('/api/') ||
pathname.includes('.') // Static files
) {
return NextResponse.next();
}
// Get token from cookies or headers
const token = request.cookies.get(STORAGE_KEYS.AUTH_TOKEN)?.value ||
request.headers.get('authorization')?.replace('Bearer ', '');
// Check if route is public
if (isPublicRoute(pathname)) {
// If user is authenticated and trying to access login, redirect to dashboard
if (token && pathname === ROUTES.LOGIN) {
const user = getUserFromToken(token);
Eif (user) {
const defaultRoute = getDefaultRouteForRole(user.role);
return NextResponse.redirect(new URL(defaultRoute, request.url));
}
}
return NextResponse.next();
}
// Check if route requires authentication
const requiredRoles = getRequiredRoles(pathname);
Eif (requiredRoles) {
// Route requires authentication
if (!token) {
// No token, redirect to login
const loginUrl = new URL(ROUTES.LOGIN, request.url);
loginUrl.searchParams.set('redirect', pathname);
return NextResponse.redirect(loginUrl);
}
// Verify token and check role
const user = getUserFromToken(token);
if (!user) {
// Invalid token, redirect to login
const loginUrl = new URL(ROUTES.LOGIN, request.url);
loginUrl.searchParams.set('redirect', pathname);
return NextResponse.redirect(loginUrl);
}
// Check if user has required role
Eif (!requiredRoles.includes(user.role)) {
// User doesn't have required role, redirect to their default dashboard
const defaultRoute = getDefaultRouteForRole(user.role);
return NextResponse.redirect(new URL(defaultRoute, request.url));
}
}
// Handle root path redirect for authenticated users
if (pathname === ROUTES.HOME && token) {
const user = getUserFromToken(token);
if (user) {
const defaultRoute = getDefaultRouteForRole(user.role);
return NextResponse.redirect(new URL(defaultRoute, request.url));
}
}
return NextResponse.next();
}
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
]};
|